from numpy import (
    ndarray,  # 数组类型
    array,  # 数组创建
    arange,  # 生成范围数组
    newaxis,  # 用于增加数组维度
    zeros,  # 创建全为零的数组
    sqrt,  # 计算平方根
    exp,  # 计算自然指数
    pi,  # 圆周率常量
)

from src.data.satellite import (
    Satellite,  # 卫星类
)


def los_channel_error_model_in_sat_and_user_pos(
        error_model_config,  # 错误模型配置参数，包含相位和方向误差等
        satellite: Satellite,  # 卫星对象，包含用户的距离、天线增益等属性
        users: list,  # 用户列表，每个元素为一个用户
) -> ndarray:
    """
    该函数模拟了卫星与用户之间的信道状态信息，并添加了卫星和用户位置上的误差。

    参数:
    - error_model_config: 包含信道误差模型的配置参数
    - satellite: 卫星对象，包含天线增益、波长、用户距离等相关信息
    - users: 用户对象列表，表示多个通信用户

    返回:
    - ndarray: 包含误差信道状态信息的复数数组

    详细说明:
    1. 该模型首先计算没有误差时的信道状态信息，包括功率比、幅度衰减和相位偏移。
    2. 然后加入卫星发射相位的误差，进一步模拟实际场景中的信道不确定性。
    3. 最后根据天线位置产生的方向矢量误差，结合之前的计算得到最终的误差信道状态信息。
    """

    # 初始化信道状态信息数组，大小为（用户数量，卫星天线数量），数据类型为复数
    channel_state_information = zeros((len(users), satellite.antenna_nr), dtype='complex')

    # 逐个用户计算信道状态信息
    for user in users:
        # 根据用户和卫星的增益、波长和距离计算信道的功率比
        power_ratio = (
                satellite.antenna_gain_linear  # 卫星的天线增益（线性值）
                * user.gain_linear  # 用户设备的增益（线性值）
                * (satellite.wavelength / (4 * pi * satellite.distance_to_users[user.idx])) ** 2  # 距离衰减
        )
        # 计算幅度衰减
        amplitude_damping = sqrt(power_ratio)

        # 根据用户与卫星的距离计算相位偏移
        phase_shift = satellite.distance_to_users[user.idx] % satellite.wavelength * 2 * pi / satellite.wavelength
        # 根据误差模型加入卫星发射相位误差
        phase_shift_error = 2 * pi / satellite.wavelength * satellite.rng.normal(loc=0, scale=error_model_config.phase_sat_error_std)

        # 计算信道状态信息（包含幅度衰减、相位偏移以及方向矢量）
        channel_state_information[user.idx, :] = (
            amplitude_damping  # 幅度衰减
            * exp(1j * phase_shift)  # 相位偏移
            * exp(1j * phase_shift_error)  # 相位误差
            * satellite.steering_vectors_to_users[user.idx]  # 卫星到用户的方向矢量
        )

    # 计算天线的方向矢量索引，以计算天线误差
    steering_idx = arange(0, satellite.antenna_nr) - (satellite.antenna_nr - 1) / 2

    # 计算天线方向矢量的误差，通过均匀分布生成误差范围
    steering_error = exp(
        steering_idx * (
            1j * 2 * pi / satellite.wavelength  # 根据波长和天线间距计算误差
            * satellite.antenna_distance  # 天线之间的距离
            * satellite.rng.uniform(low=error_model_config.uniform_error_interval['low'],  # 误差下界
                                    high=error_model_config.uniform_error_interval['high'],  # 误差上界
                                    size=(len(users), 1))  # 为每个用户生成误差
        )
    )

    # 将之前计算的信道状态信息与天线误差相乘，得到最终的误差信道状态信息
    erroneous_channel_state_to_users = channel_state_information * steering_error

    # 返回包含误差的信道状态信息
    return erroneous_channel_state_to_users
